package org.wsff.tools.api.gateway.core.monitor;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

/**
 * NamedThreadFactory
 * @author ryan
 * @version Id: NamedThreadFactory.java, v 0.1 2022-02-08 09:26 ryan Exp $$
 */
public class NamedThreadFactory implements ThreadFactory {

    private static final String             LINE = "-";
    /** A counter for the threads created by this factory. */
    private final AtomicLong                threadCounter;

    /** The thread factory wrapper */
    private ThreadFactory                   wrappedFactory;

    /** The uncaught exception handler. */
    private Thread.UncaughtExceptionHandler exceptionHandler;

    /** The thread name. */
    private String                          name;

    /** The priority. */
    private Integer                         priority;

    /** The daemon flag. */
    private Boolean                         daemon;

    public NamedThreadFactory(Builder builder) {
        if (builder.threadFactory == null) {
            this.wrappedFactory = Executors.defaultThreadFactory();
        } else {
            this.wrappedFactory = builder.threadFactory;
        }
        this.name = builder.name;
        this.priority = builder.priority;
        this.daemon = builder.daemon;
        this.exceptionHandler = builder.exceptionHandler;
        this.threadCounter = new AtomicLong();
    }

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = getWrappedFactory().newThread(r);
        initThread(thread);
        return thread;
    }

    /**
     * Gets the value of daemon.
     *
     * @return the value of daemon
     */
    public Boolean getDaemon() {
        return daemon;
    }

    /**
     * 初始化线下
     * 
     * @param thread 线程对象
     */
    private void initThread(Thread thread) {
        if (StringUtils.isNotBlank(getName())) {
            long count = threadCounter.incrementAndGet();
            thread.setName(getName() + LINE + count);
        }
        if (getPriority() != null) {
            thread.setPriority(getPriority());
        }
        if (getDaemon() != null) {
            thread.setDaemon(getDaemon());
        }
        if (getExceptionHandler() != null) {
            thread.setUncaughtExceptionHandler(getExceptionHandler());
        }
    }

    public final static class Builder {

        /** The thread factory wrapper. */
        private ThreadFactory                   threadFactory;

        /** The uncaught exception handler. */
        private Thread.UncaughtExceptionHandler exceptionHandler;

        /** The thread name. */
        private String                          name;

        /** The priority. */
        private Integer                         priority;

        /** The daemon flag. */
        private Boolean                         daemon;

        public static Builder builder() {
            return new Builder();
        }

        /**
         * Sets the factory. *
         * <p>You can use getThreadFactory() to get the value of factory</p >
         * @param factory factory
         */
        public Builder threadFactory(final ThreadFactory factory) {
            Validate.notNull(factory, "ThreadFactory must not be null!");
            this.threadFactory = factory;
            return this;
        }

        /**
         * Sets the exceptionHandler. *
         * <p>You can use getExceptionHandler() to get the value of exceptionHandler</p >
         * @param exceptionHandler exceptionHandler
         */
        public Builder exceptionHandler(final Thread.UncaughtExceptionHandler exceptionHandler) {
            Validate.notNull(exceptionHandler, "UncaughtExceptionHandler must not be null!");
            this.exceptionHandler = exceptionHandler;
            return this;
        }

        /**
         * Sets the name. *
         * <p>You can use getName() to get the value of name</p >
         * @param name name
         */
        public Builder name(String name) {
            Validate.notNull(name, "Thread name must not be null!");
            this.name = name;
            return this;
        }

        /**
         * Sets the priority. *
         * <p>You can use getPriority() to get the value of priority</p >
         * @param priority priority
         */
        public Builder priority(int priority) {
            this.priority = priority;
            return this;
        }

        /**
         * Sets the daemon. *
         * <p>You can use getDaemon() to get the value of daemon</p >
         * @param daemon daemon
         */
        public Builder daemon(boolean daemon) {
            this.daemon = daemon;
            return this;
        }

        public void reset() {
            threadFactory = null;
            exceptionHandler = null;
            name = null;
            priority = null;
            daemon = null;
        }

        public ThreadFactory build() {
            final ThreadFactory factory = new NamedThreadFactory(this);
            reset();
            return factory;
        }
    }

    /**
     * Gets the value of threadCounter.
     *
     * @return the value of threadCounter
     */
    public long getThreadCount() {
        return threadCounter.get();
    }

    /**
     * Gets the value of wrappedFactory.
     *
     * @return the value of wrappedFactory
     */
    public ThreadFactory getWrappedFactory() {
        return wrappedFactory;
    }

    /**
     * Gets the value of exceptionHandler.
     *
     * @return the value of exceptionHandler
     */
    public Thread.UncaughtExceptionHandler getExceptionHandler() {
        return exceptionHandler;
    }

    /**
     * Gets the value of name.
     *
     * @return the value of name
     */
    public String getName() {
        return name;
    }

    /**
     * Gets the value of priority.
     *
     * @return the value of priority
     */
    public Integer getPriority() {
        return priority;
    }

}
